本篇大綱:軸線的組成、建立軸線的必備工具、繪製軸線的API、ticks 刻度、XY 軸範例
講了好久終於進到軸線與刻度
了!!!前面的章節看完比例尺之後,接下來就輪到繪製軸線啦~軸線也是圖表中很重要的一部分,幾乎所有圖表都會有X軸跟Y軸
今天我們就來講講要怎麼繪製軸線吧!
首先,我們先來看看軸線是由哪些DOM元素組成的。圖表的座標軸其實是一個複雜的結構,一條座標軸包含了
< path >
一條直線,標示軸線< line >
一組沿著軸的刻度記號(ticks)< text >
每個刻度記號的標籤通常我們會使用 < g >
這個 svg 的結構標籤把整條軸線的元素包在一起,之後對 < g > 元素進行一些操作(例如:移動的時候,就能將整條軸線一起移動。)
了解軸線的組成結構後,我們來看看要建立軸線的兩大必備工具
DOM 元素 (path、line、text 等等)
我們必須要有一整組DOM元素,才能繪製出軸線
比例尺
除了 DOM 元素,也要搭配比例尺去計算出軸線的最大與最小值,才知道軸線要畫多長、刻度要怎麼計算
有了這兩個工具,我們就可以簡單來建立一條軸線了,我們先畫一條X軸試試看
// html
<div class="demo1"></div>
// js
d3.select('.demo1')
.append('svg')
.attr("width", 500)
.attr("height", 200)
.append('g')
// 設定比例尺
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
// 建立軸線
let axis = d3.axisBottom(scale);
d3.select('svg g')
.call(axis);
建立好的軸線長這樣
你一定會說:天啊這不是我要的軸線~這不是我要的軸線!為什麼會這樣呢?那是因為 svg 都是從左上方的原點由上往下繪製,因此軸線長這樣很正常。如果想要把軸線左右留點空間、移到下方的位置,我們就要 margin
跟 transform
來進行一些設定。
let width = 500,
height = 200,
margin = 10;
// 軸線建立
d3.select('.demo1')
.append('svg')
.attr("width", width)
.attr("height", height)
.append('g')
.attr('transform', `translate(${margin}, ${height-(margin*2)})`)
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, `${width-(margin*2)}`]);
let axis = d3.axisBottom(scale);
d3.select('svg g')
.call(axis);
這樣建立出來的軸線就是完美的X軸啦!
特別要注意的是,建立軸線時,我們會使用 selection.call( )
這個方法來呼叫軸線的方法,那D3提供哪幾種繪製軸線的方法呢? 我們一起來看一下
一樣先來看看官方文件~D3 提供了這些方法讓我們能繪製軸線
axisTop(scale)
、axisBottom(scale)
、axisRight(scale)
、axisLeft(scale)
這四個 API 是主要拿來建立軸線的方法,它們都是帶入 scale比例尺 作為參數,接著根據帶入的比例尺生成一個 axis generator,並包含預設的 ticks刻度設定。
Scale 參數
軸線會因為帶入的 scale比例尺 不同,而產生不同的刻度間距,例如:
如果不知道每種 scale 怎麼設定,歡迎看看上一篇的 scale 講解
刻度位置
這四個 API 之所以又分成 Top、Bottom、Right、Left 四種,只是因為生成的 ticks 方向不同~
axisTop ⇒ ticks 刻度在軸線上方
axisBottom ⇒ ticks 刻度在軸線下方
axisRight ⇒ ticks 刻度在軸線右方
axisLeft ⇒ ticks 刻度在軸線左方
除了預設好的刻度之外,D3也提供了不少讓我們能自訂 ticks 刻度的 API
我們能進行的設定包含:
現在就讓我挑幾個常用的API來講解吧!
axis.ticks( ) => 調整ticks數量
這個方法可以讓我們自行調整刻度的數量。
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale);
axis.ticks(20);
d3.select('svg g')
.call(axis);
但要注意的是,即使我們定義好想要的特定刻度數量,如果刻度的值並非完整的數值的話,D3會自行調整刻度數量,讓 ticks value 是漂亮的數字 (例如以5為單位間隔),例如:
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale);
axis.ticks(27); // 改成要27個ticks
d3.select('svg g')
.call(axis);
出來的結果是一樣的
axis.tickValues( ) => 指定想呈現的 ticks value
這個方法可以用來指定只想呈現哪些 tick values。這個方法超方便,如果我們想呈現某些特別奇怪的刻度,就可以用它來設定~
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale);
axis.tickValues([0, 25, 53, 77, 81, 95]);
d3.select('svg g')
.call(axis);
axis.tickFormat( ) => 調整 tick label 的文字
這個也是非常好用的一個API,它可以協助我們調整想要呈現的刻度文字
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale);
axis.tickFormat(function(d) {
return d + "元";
});
d3.select('svg g')
.call(axis);
axis.tickSize( )
、axis.tickSizeInner( )
、axis.tickSizeOuter( )
再來介紹的這三種方法是用來設定刻度線的長度,D3的刻度線分為 inner 跟 outer 兩種
而這三種 API 分別能設定
預設的刻度線長度是6,但我們可以透過 tickSize 的設定來加長或縮短刻度線的長度,而且會自訂把 tick value label 排到線段之後
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale)
.tickSize(30);
d3.select('svg g')
.call(axis);
接著我們也可以進行一些漂亮的設定,像是使用css 跟 tickSize 搭配,去畫出這種虛線段的軸線
我們先建立軸線
// js
let scale = d3.scaleLinear().domain([0, 100]).range([0, 400]);
let axis = d3.axisRight(scale)
.tickSize(400); // 把tick的尺寸設定成跟range的最大值一樣,就能得到滿版的軸線
d3.select('.demoDashed')
.append('svg')
.attr('width', '500')
.attr('height', '400')
.append('g')
.attr('class', 'dashed')
.attr('transform', 'translate(20, 20)')
.call(axis);
接著再設定CSS,將軸線設定為隱藏,刻度線設定為dashed
// css
g.dashed path {
display: none;
}
g.dashed line {
stroke-dasharray: 1 1;
}
這樣就完成拉~如果再用一樣的方式設定X軸,就能得到棋盤狀的格線囉!
再來如果我們想單獨設定 tickSizeOuter 或 tickSizeInner 也可以,但就要一起設定 tickPadding,否則軸線就會變成這樣
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale)
.tickSizeOuter(60);
d3.select('svg g')
.call(axis);
看到這張圖,知道接下來的 tickPadding 是做什麼用了嗎?
axis.tickPadding( ) ⇒ 調整刻度線與文字標籤的距離
你沒猜錯!tickPadding( ) 就是用來調整刻度線跟文字標籤的距離,預設的距離是3,但我們可以依照自己的需求去設定
let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);
let axis = d3.axisBottom(scale)
.tickPadding(60);
d3.select('svg g')
.call(axis);
哇賽好不容易把軸線跟刻度的API 都大致講完了,我們現在來用資料實際畫完整的 XY 軸吧!
我們這次要建立的軸線是這樣
由於有兩條軸線,因此兩條軸線都要分別設定要使用的 scale 跟 axis。我們先看到手上擁有的資料
// html
<div class="XY_Axis"></div>
// js
const svg = d3.select(".XY_Axis")
.append("svg")
.attr("width", 500)
.attr("height", 200);
// data
const data = [{x:100, y:20}, {x:18, y:30}, {x:90, Y:250}]
一般來說我們都會拿到這種陣列包物件的資料,裡面帶有多筆數值。我們把要設定X軸的資料跟設定Y軸的資料分別整理出來
// map 資料集
xData = data.map((i) => i.x); // 預期套資料後會得到 [100, 18, 90] 的陣列
yData = data.map((i) => i.y); // 預期套資料後會得到 [20, 30, 250] 的陣列
接著我們先來設定 X 軸線
// 設定要給 X 軸用的scale 跟 axis
const xScale = d3.scaleLinear()
.domain([0, d3.max(xData)])
.range([20, 480]); // X 軸繪製的viewport起點終點(寬度)
const xAxis = d3.axisBottom(xScale)
.tickFormat(function (d) {
//調整標籤樣式
return `${d} 元`;
})
// 呼叫繪製x軸、調整x軸位置
const xAxisGroup = svg.append("g")
.call(xAxis)
.attr("transform", "translate(0,180)")
再來設定 Y 軸線
// 設定要給 Y 軸用的 scale 跟 axis
const yScale = d3.scaleLinear()
.domain([0, d3.max(yData)])
.range([20, 180]); // Y 軸繪製的viewport 起點終點(高度)
const yAxis = d3.axisLeft(yScale)
.ticks(5)
// 呼叫繪製y軸、調整y軸位置
const yAxisGroup = svg.append("g")
.call(yAxis)
.attr("transform", "translate(20,0)")
這時候座標軸已經畫出來了,但你會發現Y軸的刻度跟我們平常看到的不一樣,是顛倒的?
這是因為軸線是從 svg 左上方的原點,由上往下繪製,所以當我們把 range 設定成 ([20, 180]) 時,Y軸線就是正常的從離原點20px的地方開始由上方往下建立。
為了要讓Y軸的數值能顛倒過來,我們要把 range 的值也顛倒設定
const yScale = d3.scaleLinear()
.domain([0, d3.max(yData)])
.range([180, 20]); // 顛倒
這樣就完成啦!要注意的是,由於 X 跟 Y 軸的長度分別是看 svg 寬度跟 svg 高度,因此它們兩個的 range 範圍設定也會有所不同,不要設定錯囉!
終於完整介紹完座標軸跟刻度啦!!大家明天見~
最後一樣附上本章的程式碼與圖表 Github 、 Github Page,需要的人請自行取用~